<?php

namespace Helpers;

class GmClient
{
    const PacketHeaderLen = 24;
    const PacketHeaderCheckCode = 21301;
    const PseudoWebClientUserId = 2100000000;
    const PackHeaderOpCode = 20301;
    const PacketHeaderBackId = 0;
    const PackHeaderAppId = 0;
    const PackHeaderAppCode = 0;
    const TimeoutSecs = 2;

    const PackChannel = 2;

    ////////////////////////////////////////////////////////
    private $socket_;


    function connect($ip, $port){
        $this->socket_ = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if ($this->socket_ === false)
            return false;

        if ($this->socketConnect($ip, $port)){
            return true;
        }

        @socket_close($this->socket_);
        $this->socket_ = null;
        return false;
    }

    function __destruct()
    {
        if ($this->socket_) {
            @socket_shutdown($this->socket_);
            @socket_close($this->socket_);
        }
    }


    public function socketLastError()
    {
        return @socket_last_error($this->socket_);
    }

    public function writeAndReadChatMsg($chatMsg, &$result)
    {
        $err = $this->writeChatMsg($chatMsg);
        return $err ? $this->readChatMsg($result) : $err;
    }

    /* 消息包
     struct TXsCcChatNtf : public ZAppBase
    {
        emChatChannel	 channel;
        int				 msglen;           //消息长度
        char			 chatmsg[1024];
        TXsCcChatNtf():ZAppBase(XS_CC_CHAT_NTF, sizeof(TXsCcChatNtf) )
        {
        }
    };*/

    public function writeChatMsg($chatMsg)
    {
        if (!$this->socket_)
            return false;

        $content = pack("LLLa*", self::PackChannel, 0, strlen($chatMsg) , $chatMsg);
        $header = self::packHeader(self::PackHeaderOpCode, self::PacketHeaderLen + strlen($content), self::PseudoWebClientUserId, 0, 0);

        return $this->socketWrite($header . $content) ? true : false;
    }

    public function readChatMsg(&$result)
    {
        if (!$this->socket_)
            return false;
        $result = array();
        do{
            $packet = $this->socketRead(self::PacketHeaderLen);
            if ($packet === false)
                break;

            $header = self::unpackHeader($packet);
            $packet = $this->socketRead($header['len'] - self::PacketHeaderLen);
            if ($packet === false)
                break;

            $content = unpack('Lchannel/Llen/a*result', $packet);

            $temp = $content['result'];
            if (!empty($temp)){
                $temp = substr($temp, 0, strlen($temp) - 1);
                $result[] = $temp;
            }

        }while(true);

        return true;
    }

    private function socketRead($len)
    {
        $result = "";
        while ($len > 0) {
            $r = array($this->socket_);
            $ret = @socket_select($r, $w = null, $e = null, self::TimeoutSecs);
            if (!$ret)
                return false;

            $read = @socket_read($this->socket_, $len);
            if (!$read)
                return false;

            $result .= $read;
            $len -= strlen($read);
        }

        return $result;
    }

    private function socketWrite($packet)
    {
        while (!empty($packet)) {
            $sended = @socket_write($this->socket_, $packet);
            if ($sended === false)
                return false;
            $packet = substr($packet, $sended);
        }
        return true;
    }

    private function socketConnect($ip, $port)
    {
        @socket_set_nonblock($this->socket_);
        @socket_connect($this->socket_, $ip, $port);
        @socket_set_block($this->socket_);

        $w = array($this->socket_);
        return @socket_select($r = null, $w, $e = null, self::TimeoutSecs) == 1;
    }
    /* 包头+固定格式
    struct PacketBase
    {
        PacketBase(unsigned int opcode, unsigned int len):OPCode(opcode),Len(len),user_id(sc_invalid_user_temp_id){CheckCode=sc_packetmsg_checkcode;BackID=0;}
        unsigned int    OPCode;          //操作类别
        unsigned int    Len;             //整个消息包长度
        unsigned int    user_id;         //modify by zht
        unsigned short  CheckCode;       //校验吗21301;
        unsigned short  BackID;          //备用;
    };

     struct ZAppBase : public PacketBase
    {
        ZAppBase(unsigned int opcode,unsigned int len) : PacketBase( opcode,len )
        {
            ZtyAppId = 0;
            appType  = ZAppType_NoDef;
        }

        unsigned int ZtyAppId;   //客户端的应用ID
        emZtyAppType appType;    //客户端的app应用类型
    };*/
    private static function packHeader($opCode, $packLen, $userId, $appId, $appType)
    {
        return pack('LLLSSLL', $opCode, $packLen, $userId, self::PacketHeaderCheckCode, self::PacketHeaderBackId, $appId, $appType);
    }

    private static function unpackHeader($packet)
    {
        return unpack('LopCode/Llen/LuserId/ScheckCode/SbackId/LappId/LappType', $packet);
    }
}